package kevin.framework.basic.dao.impl;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import kevin.framework.basic.common.utils.DynamicDaoSupport;
import kevin.framework.basic.common.utils.DynamicParameter;
import kevin.framework.basic.common.utils.MyUUID;
import kevin.framework.basic.common.utils.PagingResult;
import kevin.framework.basic.dao.IBaseDao;

/**
 * @ClassName: BaseDao
 * @Description: dao增删改查基类
 * @author hjwen
 * @date 2014-12-3 上午10:20:28
 * @param <T>
 *            对应的领域实体类型
 * @param <K>
 *            主键数据类型
 */
public class BaseDao<T extends Serializable, K extends Serializable> extends DynamicDaoSupport
    implements IBaseDao<T, K> {

    public final Logger logger = LoggerFactory.getLogger(getClass());
    Method setIdMethod = null;
    Method getIdMethod = null;
    // mapper.xml中的namespace
    protected String namespace;
    // sqlmap.xml定义文件中对应的sqlid
    public static final String SQLID_INSERT = "insert";
    public static final String SQLID_INSERT_BATCH = "insertBatch";
    public static final String SQLID_UPDATE = "update";
    public static final String SQLID_UPDATE_PARAM = "updateParam";
    public static final String SQLID_UPDATE_BATCH = "updateBatch";
    public static final String SQLID_DELETE = "delete";
    public static final String SQLID_DELETE_PARAM = "deleteParam";
    public static final String SQLID_DELETE_BATCH = "deleteBatch";
    public static final String SQLID_TRUNCATE = "truncate";
    public static final String SQLID_SELECT_PK = "selectPk";
    public static final String SQLID_SELECT_PARAM = "selectParam";
    public static final String SQLID_SELECT_MAP = "selectMap";
    public static final String SQLID_COUNT_PARAM = "countParam";

    public Class<T> beanClass;

    public String getNamespace() {
        return namespace;
    }

    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }

    public BaseDao() {
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType)type;
            beanClass = (Class<T>)pType.getActualTypeArguments()[0];
            try {
                setIdMethod = beanClass.getMethod("setId", String.class);
                setIdMethod.setAccessible(true);
                getIdMethod = beanClass.getMethod("getId");
                getIdMethod.setAccessible(true);
            } catch (Exception e) {
                /* logger.info("{}",e); */
            }
            this.namespace = beanClass.getName();
        }
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#insert(java.io.Serializable)
     */
    @Override
    public int insert(T entity) throws Exception {
        if (setIdMethod != null && getIdMethod != null) {
            Object value = getIdMethod.invoke(entity);
            if (value == null || StringUtils.isEmpty(String.valueOf(value))) {
                String id = MyUUID.getUUID();
                setIdMethod.invoke(entity, id);
            }

        }
        int rows = getSqlSession().insert(namespace + "." + SQLID_INSERT, entity);
        return rows;
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#update(java.io.Serializable)
     */
    @Override
    public int update(T entity) throws Exception {
        int rows = getSqlSession().update(namespace + "." + SQLID_UPDATE, entity);
        return rows;
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#updateParam(java.util.Map)
     */
    @Override
    public int updateParam(Map<String, Object> param) throws Exception {
        int rows = getSqlSession().update(namespace + "." + SQLID_UPDATE_PARAM, param);
        return rows;
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#delete(java.io.Serializable)
     */
    @Override
    public int delete(K primaryKey) throws Exception {
        int rows = getSqlSession().delete(namespace + "." + SQLID_DELETE, primaryKey);
        return rows;
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#deleteParam(java.util.Map)
     */
    @Override
    public int deleteParam(Map<String, Object> param) throws Exception {
        int rows = getSqlSession().delete(namespace + "." + SQLID_DELETE_PARAM, param);
        return rows;
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#truncate()
     */
    @Override
    public int truncate() throws Exception {
        int rows = getSqlSession().delete(namespace + "." + SQLID_TRUNCATE);
        return rows;
    }

    @Override
    public Map<K, T> selectMap(Map<String, Object> param, String keyName) throws Exception {
        return selectMap(SQLID_SELECT_MAP, param, keyName);
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#count(java.util.Map)
     */
    @Override
    public int count(Map<String, Object> param) throws Exception {
        if (param == null) {
            return getSqlSession().selectOne(namespace + "." + SQLID_COUNT_PARAM);
        }
        int result = getSqlSession().selectOne(namespace + "." + SQLID_COUNT_PARAM, param);
        return result;
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#get(java.io.Serializable)
     */
    @Override
    public T get(K primaryKey) throws Exception {
        return getSqlSession().selectOne(namespace + "." + SQLID_SELECT_PK, primaryKey);
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#selectAll()
     */
    @Override
    public List<T> selectAll() throws Exception {
        Map<String, Object> param = new HashMap<String, Object>();
        return this.selectParam(param);
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#selectParam(java.util.Map)
     */
    @Override
    public List<T> selectParam(Map<String, Object> param) throws Exception {
        // 补充排序条件
        if (param != null && param.containsKey("orderColumn")) {
            Set<Map.Entry<String, String>> keySet = ((Map<String, String>)param.get("orderColumn")).entrySet();
            List<String> temp = new ArrayList<String>();
            for (Map.Entry<String, String> entry : keySet) {
                temp.add(entry.getKey() + " " + entry.getValue());
            }
            param.remove("orderColumn");
            param.put("orderColumn", temp);
        }
        String id = namespace + "." + SQLID_SELECT_PARAM;
        return getSqlSession().selectList(id, param);
    }

    /**
     * @param sqlId
     * @param param
     * @return
     * @throws Exception
     */
    protected T selectBySqlId(String sqlId, Map<String, Object> param) throws Exception {
        if (param == null) {
            return getSqlSession().selectOne(namespace + "." + sqlId);
        }
        return getSqlSession().selectOne(namespace + "." + sqlId, param);
    }

    /**
     * @param sqlId
     * @param param
     * @param keyName
     * @return
     * @throws Exception
     */
    protected Map<K, T> selectMap(String sqlId, Map<String, Object> param, String keyName) throws Exception {
        Map<K, T> map;
        if (param == null)
            map = getSqlSession().selectMap(namespace + "." + sqlId, keyName);
        else {
            SqlSession session = getSqlSession();
            String key = namespace + "." + sqlId;
            map = session.selectMap(key, param, keyName);
        }
        return map;
    }

    /**
     * @Description: 自定义返回类型E的单一值查询
     * @param sqlId
     * @param param
     * @param handler
     */
    protected <E> E selectOneE(String sqlId, Map<String, Object> param) {
        if (param == null) {
            return getSqlSession().selectOne(namespace + "." + sqlId);
        }
        return getSqlSession().selectOne(namespace + "." + sqlId, param);
    }

    /**
     * @Description: 自定义返回类型E的单一值查询
     * @param sqlId
     * @param param
     * @param handler
     */
    protected T selectOne(String sqlId, Map<String, Object> param) {
        if (param == null) {
            return getSqlSession().selectOne(namespace + "." + sqlId);
        }
        return getSqlSession().selectOne(namespace + "." + sqlId, param);
    }

    /**
     * 自定义返回类型E列表的查询
     * 
     * @param sqlId
     * @param param
     * @return
     * @throws Exception
     */
    protected <E> List<E> selectParamE(String sqlId, Map<String, Object> param) throws Exception {
        if (param == null) {
            return getSqlSession().selectList(namespace + "." + sqlId);
        }
        return getSqlSession().selectList(namespace + "." + sqlId, param);
    }

    /**
     * 自定义返回E,F类型Map的查询
     * 
     * @param sqlId
     * @param param
     * @return
     * @throws Exception
     */
    protected <E, F> Map<E, F> selectMapE(String sqlId, Map<String, Object> param, String mapKey) throws Exception {
        if (param == null) {
            return getSqlSession().selectMap(namespace + "." + sqlId, mapKey);
        }
        return getSqlSession().selectMap(namespace + "." + sqlId, param, mapKey);
    }

    /**
     * @param sqlId
     * @param param
     * @return
     * @throws Exception
     */
    protected List<T> selectListBySqlId(String sqlId, Map<String, Object> param) throws Exception {
        // 补充排序条件
        if (param != null && param.containsKey("orderColumn")) {
            Set<Map.Entry<String, String>> keySet = ((Map<String, String>)param.get("orderColumn")).entrySet();
            List<String> temp = new ArrayList<String>();
            for (Map.Entry<String, String> entry : keySet) {
                temp.add(entry.getKey() + " " + entry.getValue());
            }
            param.remove("orderColumn");
            param.put("orderColumn", temp);
        }
        return getSqlSession().selectList(namespace + "." + sqlId, param);
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#selectPagination(kevin.framework.basic.common.utils.DynamicParameter)
     */
    @Override
    public PagingResult<T> selectPagination(DynamicParameter DynamicParameter) throws Exception {

        int page = DynamicParameter.getPage() == null || "".equals(DynamicParameter.getPage()) ? 1
            : DynamicParameter.getPage(); // 默认为第一页
        int size = DynamicParameter.getSize() == null || "".equals(DynamicParameter.getSize()) ? 15
            : DynamicParameter.getSize();

        RowBounds rowBounds = new RowBounds((page - 1) * size, size);
        Map<String, Object> param = DynamicParameter.getParams();
        List<String> orderString = DynamicParameter.getOrderColumn();
        if (param == null) {
            param = new HashMap<String, Object>();
        }
        if (orderString.size() > 0) {
            param.put("orderColumn", orderString);
        }
        String nameSpaceSql = namespace + "." + SQLID_SELECT_PARAM;
        PageHelper.startPage(page, size);
        List<T> resultList = getSqlSession().selectList(nameSpaceSql, param, rowBounds);
        PageInfo<T> pageResult = new PageInfo<T>(resultList);
        long totalSize = pageResult.getTotal();
        PagingResult<T> pagingResult = new PagingResult<T>();
        pagingResult.setCurrentPage(page);
        pagingResult.setTotalSize(totalSize);
        List<T> lise = pageResult.getList();
        pagingResult.setResultList(lise);
        return pagingResult;
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#insertBatch(java.util.List)
     */
    @Override
    public int insertBatch(List<T> list) throws Exception {
        return getSqlSession().insert(namespace + "." + SQLID_INSERT_BATCH, list);

    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#updateBatch(java.util.List)
     */
    @Override
    public int updateBatch(List<T> list) throws Exception {
        int rows = 0;
        for (T t : list) {
            rows = rows + getSqlSession().update(namespace + "." + SQLID_UPDATE_BATCH, t);
        }
        return rows;
    }

    /* (non-Javadoc)
     * @see kevin.framework.basic.dao.IBaseDao#deleteBatch(java.util.List)
     */
    @Override
    public int deleteBatch(List<K> list) throws Exception {
        if (list.size() == 1) {
            return this.delete(list.get(0));
        }
        return getSqlSession().delete(namespace + "." + SQLID_DELETE_BATCH, list);
    }

}
